package generador;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Point;
import java.util.Random;
import java.util.Vector;
import javax.swing.JOptionPane;

import principal.CeldaSudoku;

/**
 * genera un sudoku completo, almacenado en el array "sudoku"
 * @author trino
 */
public class GeneraSudoku {

	private boolean debug = false;
	// Vector<datosCelda> secuencia = new Vector<datosCelda>(30, 1);
	// ...el array sudoku organizado por columnas, filas
	CeldaSudoku[][] sudoku;
	// boolean[][] sudokuVisible;
	private String[][] valoresMalos = { { "", "", "", "", "", "", "", "", "" },
			{ "", "", "", "", "", "", "", "", "" },
			{ "", "", "", "", "", "", "", "", "" },
			{ "", "", "", "", "", "", "", "", "" },
			{ "", "", "", "", "", "", "", "", "" },
			{ "", "", "", "", "", "", "", "", "" },
			{ "", "", "", "", "", "", "", "", "" },
			{ "", "", "", "", "", "", "", "", "" },
			{ "", "", "", "", "", "", "", "", "" } };
	public String cantSudoku = ""; // cantidad de numeros a devolver
	Random rnd;


	/**
	 * genera un sudoku completo, y devuelve un array de cant de cifras
	 * 
	 * @param cant
	 */
	public GeneraSudoku(CeldaSudoku[][] sudo) {
		sudoku = sudo;
//		nivel = n;
		do{
			if (genera()) break;
		}while (true);
//		fijaNivelPartida(nivel);
	}

	/**
	 * genera sudoku completo
	 */
	private boolean genera() {
		Celda celda = new Celda();
		///  limito a x el numero de intentos fallidos
		int intentos = 0;
		// ...borro la matriz
		for (int y = 0; y < 9; y++)
			for (int x = 0; x < 9; x++) {
				sudoku[y][x].valor = 0;
				valoresMalos[y][x] = "";
			}
		// ................-arranco el random integers para hacerlo aleatorio
		for (int y = 0; y < 9; y++) {
			for (int x = 0; x < 9; x++) {
				// ....genera el sudoku de forma lineal
				celda.setX(x);
				celda.setY(y);
				celda.setValor(0); // borro cualquier valor
				sudoku[y][x].error = false;
				// ....busca numero hasta que sea valido
				if (!buscaNumeroValido(celda)) { // .... si no encuentra un
					// numero valido RETROCEDO!!
					intentos ++;
					if (intentos>3000) return false;
					// ...borro el numero guardado y la huella
					sudoku[celda.getY()][celda.getX()].valor = 0; // ...anulo el
					// valor en
					// sudoku
					valoresMalos[celda.getY()][celda.getX()] = "";
					// ...retrocedo posicion y actualizo el dato malo de la
					// casilla anterior
					celda.retrocede1Posicion();
					// /recojo el valor anterior para marcarlo como malo
					celda.setValor(sudoku[celda.getY()][celda.getX()].valor);
					setNumeroMalo(celda); // ...marco el numero anterior como no
					// valido
					celda.retrocede1Posicion();// ..retrocedo 2 para adelantar
					// una
					// debug("<Retrocedo al nivel y="+celda.getY()+" x="+celda.getX());
					if (x > 1) {
						x -= 2;
					} else if (x == 1) {
						x = 8;
						y--;
					} else {
						x = 7;
						y--;
					}
				} // end if numeroValido
				else {
					sudoku[celda.getY()][celda.getX()].valor = celda.getValor();
				} // / final bucle buscaNumValido
			} // final bucle x
		} // final bucle y
		// /despeja memoria
		rnd = new Random();
		return true;
	}
	

	/**
	 * busca numero valido, si lo encuentra, lo graba, sino manda disminuir la
	 * cuenta un numero. Actualiza la mascara
	 * 
	 * @param y
	 * @param x
	 * @return true si lo encuentra, false si no.
	 */
	private boolean buscaNumeroValido(Celda celda) {
		// ...genera un numero de acuerdo a la mascara de numeros malos
		// ...si son todos malos no busques y sal
		if (sonTodosMalos(celda))
			return false;
		// ....busca numero hasta que sea valido
		for (int a = 0; a < 9; a++) {
			generaNumValido(celda);
			if (sonTodosMalos(celda))
				return false;
			// System.out.print("-" + celda.getValor() + "+"
			// + valoresMalos[celda.getY()][celda.getX()]);
			if (!esRepetido(celda)) { // ..si no esta en fila, columna y
				// cuadrado
				// System.out.print("<Encontrado ");
				return true; // sale bucle numero valido
			} else {
				setNumeroMalo(celda);
				// System.out.print("<No ");
			}
		}
		return false;
	}

	/**
	 * genera numero aleatorio, y lo comprueba con la mascara, si esta en la
	 * mascara, genera otro numero si no esta retorna elnumero
	 * 
	 * @param mascara
	 * @return true si encuentra numero, false si no
	 */
	private boolean generaNumValido(Celda celda) {
		// .si la mascara tiene 9 caracteres, esta llena
		String mascara = valoresMalos[celda.getY()][celda.getX()];
		if (mascara.length() > 8) {
			celda.setValor(0);
			return false;
		}
		int valor = 0, z = 0;
		char a;
		Random rand = new Random();
		rand.nextInt(9);
		rand.nextInt(9);
		rand.nextInt(9);
		do { // .................limito el bucle a 20 por si las moscas.......
			valor = rand.nextInt(9) + 1;
			z++;
			if (mascara.length() < 1) { // /.. si mascara vacia, acepta el
				// primer numero
				celda.setValor(valor);
				return true;
			}
			a = (char) (valor + 48);
			// debug(mascara+"$"+a);
			if (mascara.indexOf(a) < 0) {
				celda.setValor(valor);
				// debug("$"+a+"$"+valor);
				return true;
			}
		} while (z < 50);
		celda.setValor(valor);
		return false;
	}

	/**
	 * comprueba si el numero entrado ya esta en alguna posicion conflictiva
	 * 
	 * @param num
	 * @param posX
	 * @param posY
	 * @return true si es repetido, false si no lo es
	 */
	private boolean esRepetido(Celda cd) {
		// ....comprueba si ha estado utilizado
		String mascara = valoresMalos[cd.getY()][cd.getX()];
		if (mascara.length() > 8)
			return true;
		// ...comprueba lineas
		for (int x = 0; x < 9; x++) {
			if (sudoku[cd.getY()][x].valor == cd.getValor())
				return true;
		}
		// ...comprueba columnas
		for (int y = 0; y < 9; y++) {
			if (sudoku[y][cd.getX()].valor == cd.getValor())
				return true;
		}
		// ...comprueba area
		int ax, ay;
		if (cd.getX() > 5)
			ax = 6;
		else if (cd.getX() > 2)
			ax = 3;
		else
			ax = 0;
		if (cd.getY() > 5)
			ay = 6;
		else if (cd.getY() > 2)
			ay = 3;
		else
			ay = 0;
		for (int y = ay; y < ay + 3; y++) {
			for (int x = ax; x < ax + 3; x++) {
				if (sudoku[y][x].valor == cd.getValor())
					return true;
			}
		}
		// ...ha pasado todos los filtros
		return false;
	}

	private void setNumeroMalo(Celda cd) {
		// /.....comprueba que no este como numero malo
		if (valoresMalos[cd.getY()][cd.getX()].indexOf(String.valueOf(cd
				.getValor())) > -1) // .esta
			return;
		valoresMalos[cd.getY()][cd.getX()] = valoresMalos[cd.getY()][cd.getX()]
				.concat(String.valueOf(cd.getValor()));
	}

	/**
	 * si todos los numeros han sido analizados
	 * 
	 * @param y
	 * @param x
	 * @return true si todos han sido utilizados, false si queda alguno por ver
	 */
	private boolean sonTodosMalos(Celda cd) {
		String mascara = valoresMalos[cd.getY()][cd.getX()];
		if (mascara.length() == 0)
			return false;
		for (int z = 0; z < 9; z++)
			if (mascara.indexOf((char) z) < 0)
				return false;
		// debug();
		// System.out.print(" ***Mascara completa y=" + cd.getY() + "/x="
		// + cd.getX() + " " + mascara);
		return true;
	}
	
	/*
	 * rutina de debug
	 */
	private void debug(String a) {
		if (debug)
			System.out.println(a);
	}
}
